﻿using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.TypeSpec.Generator.ClientModel;
using Microsoft.TypeSpec.Generator.ClientModel.Providers;
using Microsoft.TypeSpec.Generator.Primitives;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Snippets;
using Microsoft.TypeSpec.Generator.Statements;

namespace OpenAILibraryPlugin.Visitors
{
    /// <summary>
    /// A visitor to add the <see cref="ExperimentalAttribute"/> to types, properties, and methods that are not stable.
    /// </summary>
    public class ExperimentalAttributeVisitor : ScmLibraryVisitor
    {
        private const string _realtimeNamespace = "OpenAI.Realtime";
        private static readonly AttributeStatement _experimental001Attribute = new(typeof(ExperimentalAttribute), Snippet.Literal("OPENAI001"));
        private static readonly AttributeStatement _experimental002Attribute = new(typeof(ExperimentalAttribute), Snippet.Literal("OPENAI002"));
        private static readonly AttributeStatement _experimentalCUA001Attribute = new(typeof(ExperimentalAttribute), Snippet.Literal("OPENAICUA001"));

        // Stable classes without ExperimentalAttribute
        private static readonly HashSet<string> _stableClasses = new(StringComparer.OrdinalIgnoreCase)
        {
            "OpenAI.Audio.AudioClient",
            "OpenAI.Audio.AudioTimestampGranularities",
            "OpenAI.Audio.AudioTranscription",
            "OpenAI.Audio.AudioTranscriptionFormat",
            "OpenAI.Audio.AudioTranscriptionOptions",
            "OpenAI.Audio.AudioTranslation",
            "OpenAI.Audio.AudioTranslationFormat",
            "OpenAI.Audio.AudioTranslationOptions",
            "OpenAI.Audio.GeneratedSpeechFormat",
            "OpenAI.Audio.GeneratedSpeechVoice",
            "OpenAI.Audio.OpenAIAudioModelFactory",
            "OpenAI.Audio.SpeechGenerationOptions",
            "OpenAI.Audio.TranscribedSegment",
            "OpenAI.Audio.TranscribedWord",
            "OpenAI.Chat.AssistantChatMessage",
            "OpenAI.Chat.ChatClient",
            "OpenAI.Chat.ChatCompletion",
            "OpenAI.Chat.ChatCompletionOptions",
            "OpenAI.Chat.ChatFinishReason",
            "OpenAI.Chat.ChatFunction",
            "OpenAI.Chat.ChatFunctionCall",
            "OpenAI.Chat.ChatFunctionChoice",
            "OpenAI.Chat.ChatImageDetailLevel",
            "OpenAI.Chat.ChatInputTokenUsageDetails",
            "OpenAI.Chat.ChatMessage",
            "OpenAI.Chat.ChatMessageContent",
            "OpenAI.Chat.ChatMessageContentPart",
            "OpenAI.Chat.ChatMessageContentPartKind",
            "OpenAI.Chat.ChatMessageRole",
            "OpenAI.Chat.ChatOutputTokenUsageDetails",
            "OpenAI.Chat.ChatResponseFormat",
            "OpenAI.Chat.ChatTokenLogProbabilityDetails",
            "OpenAI.Chat.ChatTokenTopLogProbabilityDetails",
            "OpenAI.Chat.ChatTokenUsage",
            "OpenAI.Chat.ChatTool",
            "OpenAI.Chat.ChatToolCall",
            "OpenAI.Chat.ChatToolCallKind",
            "OpenAI.Chat.ChatToolChoice",
            "OpenAI.Chat.ChatToolKind",
            "OpenAI.Chat.FunctionChatMessage",
            "OpenAI.Chat.OpenAIChatModelFactory",
            "OpenAI.Chat.StreamingChatCompletionUpdate",
            "OpenAI.Chat.StreamingChatFunctionCallUpdate",
            "OpenAI.Chat.StreamingChatToolCallUpdate",
            "OpenAI.Chat.SystemChatMessage",
            "OpenAI.Chat.ToolChatMessage",
            "OpenAI.Chat.UserChatMessage",
            "OpenAI.Embeddings.EmbeddingClient",
            "OpenAI.Embeddings.EmbeddingGenerationOptions",
            "OpenAI.Embeddings.EmbeddingTokenUsage",
            "OpenAI.Embeddings.OpenAIEmbedding",
            "OpenAI.Embeddings.OpenAIEmbeddingCollection",
            "OpenAI.Embeddings.OpenAIEmbeddingsModelFactory",
            "OpenAI.Files.FileDeletionResult",
            "OpenAI.Files.FilePurpose",
            "OpenAI.Files.FileStatus",
            "OpenAI.Files.FileUploadPurpose",
            "OpenAI.Files.OpenAIFile",
            "OpenAI.Files.OpenAIFileClient",
            "OpenAI.Files.OpenAIFileCollection",
            "OpenAI.Files.OpenAIFilesModelFactory",
            "OpenAI.Images.GeneratedImage",
            "OpenAI.Images.GeneratedImageCollection",
            "OpenAI.Images.GeneratedImageFormat",
            "OpenAI.Images.GeneratedImageQuality",
            "OpenAI.Images.GeneratedImageSize",
            "OpenAI.Images.GeneratedImageStyle",
            "OpenAI.Images.ImageClient",
            "OpenAI.Images.ImageEditOptions",
            "OpenAI.Images.ImageGenerationOptions",
            "OpenAI.Images.ImageVariationOptions",
            "OpenAI.Images.OpenAIImagesModelFactory",
            "OpenAI.Models.ModelDeletionResult",
            "OpenAI.Models.OpenAIModel",
            "OpenAI.Models.OpenAIModelClient",
            "OpenAI.Models.OpenAIModelCollection",
            "OpenAI.Models.OpenAIModelsModelFactory",
            "OpenAI.Moderations.ModerationCategory",
            "OpenAI.Moderations.ModerationClient",
            "OpenAI.Moderations.ModerationResult",
            "OpenAI.Moderations.ModerationResultCollection",
            "OpenAI.Moderations.OpenAIModerationsModelFactory",
            "OpenAI.OpenAIClient",
            "OpenAI.OpenAIClientOptions",
        };

        // Stable properties without ExperimentalAttribute
        private static readonly HashSet<string> _stableProperties = new(StringComparer.OrdinalIgnoreCase)
        {
            "AssistantChatMessage.FunctionCall",
            "AssistantChatMessage.ParticipantName",
            "AssistantChatMessage.Refusal",
            "AssistantChatMessage.ToolCalls",
            "AudioClient.Pipeline",
            "AudioTranscription.Duration",
            "AudioTranscription.Language",
            "AudioTranscription.Segments",
            "AudioTranscription.Text",
            "AudioTranscription.Words",
            "AudioTranscriptionFormat.Simple",
            "AudioTranscriptionFormat.Srt",
            "AudioTranscriptionFormat.Text",
            "AudioTranscriptionFormat.Verbose",
            "AudioTranscriptionFormat.Vtt",
            "AudioTranscriptionOptions.Language",
            "AudioTranscriptionOptions.Prompt",
            "AudioTranscriptionOptions.ResponseFormat",
            "AudioTranscriptionOptions.Temperature",
            "AudioTranscriptionOptions.TimestampGranularities",
            "AudioTranslation.Duration",
            "AudioTranslation.Language",
            "AudioTranslation.Segments",
            "AudioTranslation.Text",
            "AudioTranslationFormat.Simple",
            "AudioTranslationFormat.Srt",
            "AudioTranslationFormat.Text",
            "AudioTranslationFormat.Verbose",
            "AudioTranslationFormat.Vtt",
            "AudioTranslationOptions.Prompt",
            "AudioTranslationOptions.ResponseFormat",
            "AudioTranslationOptions.Temperature",
            "ChatClient.Pipeline",
            "ChatCompletion.Content",
            "ChatCompletion.ContentTokenLogProbabilities",
            "ChatCompletion.CreatedAt",
            "ChatCompletion.FinishReason",
            "ChatCompletion.FunctionCall",
            "ChatCompletion.Id",
            "ChatCompletion.Model",
            "ChatCompletion.Refusal",
            "ChatCompletion.RefusalTokenLogProbabilities",
            "ChatCompletion.Role",
            "ChatCompletion.SystemFingerprint",
            "ChatCompletion.ToolCalls",
            "ChatCompletion.Usage",
            "ChatCompletionOptions.AllowParallelToolCalls",
            "ChatCompletionOptions.EndUserId",
            "ChatCompletionOptions.FrequencyPenalty",
            "ChatCompletionOptions.FunctionChoice",
            "ChatCompletionOptions.Functions",
            "ChatCompletionOptions.IncludeLogProbabilities",
            "ChatCompletionOptions.LogitBiases",
            "ChatCompletionOptions.MaxOutputTokenCount",
            "ChatCompletionOptions.Metadata",
            "ChatCompletionOptions.PresencePenalty",
            "ChatCompletionOptions.ResponseFormat",
            "ChatCompletionOptions.StopSequences",
            "ChatCompletionOptions.StoredOutputEnabled",
            "ChatCompletionOptions.Temperature",
            "ChatCompletionOptions.ToolChoice",
            "ChatCompletionOptions.Tools",
            "ChatCompletionOptions.TopLogProbabilityCount",
            "ChatCompletionOptions.TopP",
            "ChatFunction.FunctionDescription",
            "ChatFunction.FunctionName",
            "ChatFunction.FunctionParameters",
            "ChatFunctionCall.FunctionArguments",
            "ChatFunctionCall.FunctionName",
            "ChatImageDetailLevel.Auto",
            "ChatImageDetailLevel.High",
            "ChatImageDetailLevel.Low",
            "ChatInputTokenUsageDetails.AudioTokenCount",
            "ChatInputTokenUsageDetails.CachedTokenCount",
            "ChatMessage.Content",
            "ChatMessageContentPart.ImageBytes",
            "ChatMessageContentPart.ImageBytesMediaType",
            "ChatMessageContentPart.ImageDetailLevel",
            "ChatMessageContentPart.ImageUri",
            "ChatMessageContentPart.Kind",
            "ChatMessageContentPart.Refusal",
            "ChatMessageContentPart.Text",
            "ChatOutputTokenUsageDetails.AudioTokenCount",
            "ChatOutputTokenUsageDetails.ReasoningTokenCount",
            "ChatTokenLogProbabilityDetails.LogProbability",
            "ChatTokenLogProbabilityDetails.Token",
            "ChatTokenLogProbabilityDetails.TopLogProbabilities",
            "ChatTokenLogProbabilityDetails.Utf8Bytes",
            "ChatTokenTopLogProbabilityDetails.LogProbability",
            "ChatTokenTopLogProbabilityDetails.Token",
            "ChatTokenTopLogProbabilityDetails.Utf8Bytes",
            "ChatTokenUsage.InputTokenCount",
            "ChatTokenUsage.InputTokenDetails",
            "ChatTokenUsage.OutputTokenCount",
            "ChatTokenUsage.OutputTokenDetails",
            "ChatTokenUsage.TotalTokenCount",
            "ChatTool.FunctionDescription",
            "ChatTool.FunctionName",
            "ChatTool.FunctionParameters",
            "ChatTool.FunctionSchemaIsStrict",
            "ChatTool.Kind",
            "ChatToolCall.FunctionArguments",
            "ChatToolCall.FunctionName",
            "ChatToolCall.Id",
            "ChatToolCall.Kind",
            "EmbeddingClient.Pipeline",
            "EmbeddingGenerationOptions.Dimensions",
            "EmbeddingGenerationOptions.EndUserId",
            "EmbeddingTokenUsage.InputTokenCount",
            "EmbeddingTokenUsage.TotalTokenCount",
            "FileDeletionResult.Deleted",
            "FileDeletionResult.FileId",
            "FileUploadPurpose.Assistants",
            "FileUploadPurpose.Batch",
            "FileUploadPurpose.FineTune",
            "FileUploadPurpose.Vision",
            "FunctionChatMessage.FunctionName",
            "GeneratedImage.ImageBytes",
            "GeneratedImage.ImageUri",
            "GeneratedImage.RevisedPrompt",
            "GeneratedImageCollection.CreatedAt",
            "GeneratedImageFormat.Bytes",
            "GeneratedImageFormat.Uri",
            "GeneratedImageQuality.High",
            "GeneratedImageQuality.Standard",
            "GeneratedImageStyle.Natural",
            "GeneratedImageStyle.Vivid",
            "GeneratedSpeechFormat.Aac",
            "GeneratedSpeechFormat.Flac",
            "GeneratedSpeechFormat.Mp3",
            "GeneratedSpeechFormat.Opus",
            "GeneratedSpeechFormat.Pcm",
            "GeneratedSpeechFormat.Wav",
            "GeneratedSpeechVoice.Alloy",
            "GeneratedSpeechVoice.Echo",
            "GeneratedSpeechVoice.Fable",
            "GeneratedSpeechVoice.Nova",
            "GeneratedSpeechVoice.Onyx",
            "GeneratedSpeechVoice.Shimmer",
            "ImageClient.Pipeline",
            "ImageEditOptions.EndUserId",
            "ImageEditOptions.ResponseFormat",
            "ImageEditOptions.Size",
            "ImageGenerationOptions.EndUserId",
            "ImageGenerationOptions.Quality",
            "ImageGenerationOptions.ResponseFormat",
            "ImageGenerationOptions.Size",
            "ImageGenerationOptions.Style",
            "ImageVariationOptions.EndUserId",
            "ImageVariationOptions.ResponseFormat",
            "ImageVariationOptions.Size",
            "ModelDeletionResult.Deleted",
            "ModelDeletionResult.ModelId",
            "ModerationCategory.Flagged",
            "ModerationCategory.Score",
            "ModerationClient.Pipeline",
            "ModerationResult.Flagged",
            "ModerationResult.Harassment",
            "ModerationResult.HarassmentThreatening",
            "ModerationResult.Hate",
            "ModerationResult.HateThreatening",
            "ModerationResult.Illicit",
            "ModerationResult.IllicitViolent",
            "ModerationResult.SelfHarm",
            "ModerationResult.SelfHarmInstructions",
            "ModerationResult.SelfHarmIntent",
            "ModerationResult.Sexual",
            "ModerationResult.SexualMinors",
            "ModerationResult.Violence",
            "ModerationResult.ViolenceGraphic",
            "ModerationResultCollection.Id",
            "ModerationResultCollection.Model",
            "OpenAIClient.Pipeline",
            "OpenAIClientOptions.Endpoint",
            "OpenAIClientOptions.OrganizationId",
            "OpenAIClientOptions.ProjectId",
            "OpenAIClientOptions.UserAgentApplicationId",
            "OpenAIEmbedding.Index",
            "OpenAIEmbeddingCollection.Model",
            "OpenAIEmbeddingCollection.Usage",
            "OpenAIFile.CreatedAt",
            "OpenAIFile.Filename",
            "OpenAIFile.Id",
            "OpenAIFile.Purpose",
            "OpenAIFile.SizeInBytes",
            "OpenAIFile.Status",
            "OpenAIFile.StatusDetails",
            "OpenAIFileClient.Pipeline",
            "OpenAIModel.CreatedAt",
            "OpenAIModel.Id",
            "OpenAIModel.OwnedBy",
            "OpenAIModelClient.Pipeline",
            "SpeechGenerationOptions.ResponseFormat",
            "SpeechGenerationOptions.SpeedRatio",
            "StreamingChatCompletionUpdate.CompletionId",
            "StreamingChatCompletionUpdate.ContentTokenLogProbabilities",
            "StreamingChatCompletionUpdate.ContentUpdate",
            "StreamingChatCompletionUpdate.CreatedAt",
            "StreamingChatCompletionUpdate.FinishReason",
            "StreamingChatCompletionUpdate.FunctionCallUpdate",
            "StreamingChatCompletionUpdate.Model",
            "StreamingChatCompletionUpdate.RefusalTokenLogProbabilities",
            "StreamingChatCompletionUpdate.RefusalUpdate",
            "StreamingChatCompletionUpdate.Role",
            "StreamingChatCompletionUpdate.SystemFingerprint",
            "StreamingChatCompletionUpdate.ToolCallUpdates",
            "StreamingChatCompletionUpdate.Usage",
            "StreamingChatFunctionCallUpdate.FunctionArgumentsUpdate",
            "StreamingChatFunctionCallUpdate.FunctionName",
            "StreamingChatToolCallUpdate.FunctionArgumentsUpdate",
            "StreamingChatToolCallUpdate.FunctionName",
            "StreamingChatToolCallUpdate.Index",
            "StreamingChatToolCallUpdate.Kind",
            "StreamingChatToolCallUpdate.ToolCallId",
            "SystemChatMessage.ParticipantName",
            "ToolChatMessage.ToolCallId",
            "TranscribedSegment.AverageLogProbability",
            "TranscribedSegment.CompressionRatio",
            "TranscribedSegment.EndTime",
            "TranscribedSegment.Id",
            "TranscribedSegment.NoSpeechProbability",
            "TranscribedSegment.SeekOffset",
            "TranscribedSegment.StartTime",
            "TranscribedSegment.Temperature",
            "TranscribedSegment.Text",
            "TranscribedSegment.TokenIds",
            "TranscribedWord.EndTime",
            "TranscribedWord.StartTime",
            "TranscribedWord.Word",
            "UserChatMessage.ParticipantName",
        };

        // Stable methods without ExperimentalAttribute
        private static readonly HashSet<string> _stableMethods = new(StringComparer.OrdinalIgnoreCase)
        {
            "AudioClient.GenerateSpeech|BinaryContent|RequestOptions",
            "AudioClient.GenerateSpeech|string|GeneratedSpeechVoice|SpeechGenerationOptions|CancellationToken",
            "AudioClient.GenerateSpeechAsync|BinaryContent|RequestOptions",
            "AudioClient.GenerateSpeechAsync|string|GeneratedSpeechVoice|SpeechGenerationOptions|CancellationToken",
            "AudioClient.TranscribeAudio|BinaryContent|string|RequestOptions",
            "AudioClient.TranscribeAudio|Stream|string|AudioTranscriptionOptions|CancellationToken",
            "AudioClient.TranscribeAudio|string|AudioTranscriptionOptions",
            "AudioClient.TranscribeAudioAsync|BinaryContent|string|RequestOptions",
            "AudioClient.TranscribeAudioAsync|Stream|string|AudioTranscriptionOptions|CancellationToken",
            "AudioClient.TranscribeAudioAsync|string|AudioTranscriptionOptions",
            "AudioClient.TranslateAudio|BinaryContent|string|RequestOptions",
            "AudioClient.TranslateAudio|Stream|string|AudioTranslationOptions|CancellationToken",
            "AudioClient.TranslateAudio|string|AudioTranslationOptions",
            "AudioClient.TranslateAudioAsync|BinaryContent|string|RequestOptions",
            "AudioClient.TranslateAudioAsync|Stream|string|AudioTranslationOptions|CancellationToken",
            "AudioClient.TranslateAudioAsync|string|AudioTranslationOptions",
            "AudioTranscriptionFormat.Equals|AudioTranscriptionFormat",
            "AudioTranscriptionFormat.Equals|object",
            "AudioTranscriptionFormat.GetHashCode",
            "AudioTranscriptionFormat.operator !=|AudioTranscriptionFormat|AudioTranscriptionFormat",
            "AudioTranscriptionFormat.operator ==|AudioTranscriptionFormat|AudioTranscriptionFormat",
            "AudioTranscriptionFormat.operator implicit AudioTranscriptionFormat|string",
            "AudioTranscriptionFormat.ToString",
            "AudioTranslationFormat.Equals|AudioTranslationFormat",
            "AudioTranslationFormat.Equals|object",
            "AudioTranslationFormat.GetHashCode",
            "AudioTranslationFormat.operator !=|AudioTranslationFormat|AudioTranslationFormat",
            "AudioTranslationFormat.operator ==|AudioTranslationFormat|AudioTranslationFormat",
            "AudioTranslationFormat.operator implicit AudioTranslationFormat|string",
            "AudioTranslationFormat.ToString",
            "ChatClient.CompleteChat|BinaryContent|RequestOptions",
            "ChatClient.CompleteChat|ChatMessage[]",
            "ChatClient.CompleteChat|IEnumerable<ChatMessage>|ChatCompletionOptions|CancellationToken",
            "ChatClient.CompleteChatAsync|BinaryContent|RequestOptions",
            "ChatClient.CompleteChatAsync|ChatMessage[]",
            "ChatClient.CompleteChatAsync|IEnumerable<ChatMessage>|ChatCompletionOptions|CancellationToken",
            "ChatClient.CompleteChatStreaming|ChatMessage[]",
            "ChatClient.CompleteChatStreaming|IEnumerable<ChatMessage>|ChatCompletionOptions|CancellationToken",
            "ChatClient.CompleteChatStreamingAsync|ChatMessage[]",
            "ChatClient.CompleteChatStreamingAsync|IEnumerable<ChatMessage>|ChatCompletionOptions|CancellationToken",
            "ChatFunctionChoice.CreateAutoChoice",
            "ChatFunctionChoice.CreateNamedChoice|string",
            "ChatFunctionChoice.CreateNoneChoice",
            "ChatImageDetailLevel.Equals|ChatImageDetailLevel",
            "ChatImageDetailLevel.Equals|object",
            "ChatImageDetailLevel.GetHashCode",
            "ChatImageDetailLevel.operator !=|ChatImageDetailLevel|ChatImageDetailLevel",
            "ChatImageDetailLevel.operator ==|ChatImageDetailLevel|ChatImageDetailLevel",
            "ChatImageDetailLevel.operator implicit ChatImageDetailLevel|string",
            "ChatImageDetailLevel.ToString",
            "ChatMessage.CreateAssistantMessage|ChatCompletion",
            "ChatMessage.CreateAssistantMessage|ChatFunctionCall",
            "ChatMessage.CreateAssistantMessage|ChatMessageContentPart[]",
            "ChatMessage.CreateAssistantMessage|IEnumerable<ChatMessageContentPart>",
            "ChatMessage.CreateAssistantMessage|IEnumerable<ChatToolCall>",
            "ChatMessage.CreateAssistantMessage|string",
            "ChatMessage.CreateFunctionMessage|string|string",
            "ChatMessage.CreateSystemMessage|ChatMessageContentPart[]",
            "ChatMessage.CreateSystemMessage|IEnumerable<ChatMessageContentPart>",
            "ChatMessage.CreateSystemMessage|string",
            "ChatMessage.CreateToolMessage|string|ChatMessageContentPart[]",
            "ChatMessage.CreateToolMessage|string|IEnumerable<ChatMessageContentPart>",
            "ChatMessage.CreateToolMessage|string|string",
            "ChatMessage.CreateUserMessage|ChatMessageContentPart[]",
            "ChatMessage.CreateUserMessage|IEnumerable<ChatMessageContentPart>",
            "ChatMessage.CreateUserMessage|string",
            "ChatMessage.operator implicit ChatMessage|string",
            "ChatMessageContentPart.CreateImagePart|BinaryData|string|ChatImageDetailLevel?",
            "ChatMessageContentPart.CreateImagePart|Uri|ChatImageDetailLevel?",
            "ChatMessageContentPart.CreateRefusalPart|string",
            "ChatMessageContentPart.CreateTextPart|string",
            "ChatMessageContentPart.operator implicit ChatMessageContentPart|string",
            "ChatResponseFormat.CreateJsonObjectFormat",
            "ChatResponseFormat.CreateJsonSchemaFormat|string|BinaryData|string|bool?",
            "ChatResponseFormat.CreateTextFormat",
            "ChatTool.CreateFunctionTool|string|string|BinaryData|bool?",
            "ChatToolCall.CreateFunctionToolCall|string|string|BinaryData",
            "ChatToolChoice.CreateAutoChoice",
            "ChatToolChoice.CreateFunctionChoice|string",
            "ChatToolChoice.CreateNoneChoice",
            "ChatToolChoice.CreateRequiredChoice",
            "EmbeddingClient.GenerateEmbedding|string|EmbeddingGenerationOptions|CancellationToken",
            "EmbeddingClient.GenerateEmbeddingAsync|string|EmbeddingGenerationOptions|CancellationToken",
            "EmbeddingClient.GenerateEmbeddings|BinaryContent|RequestOptions",
            "EmbeddingClient.GenerateEmbeddings|IEnumerable<ReadOnlyMemory<int>>|EmbeddingGenerationOptions|CancellationToken",
            "EmbeddingClient.GenerateEmbeddings|IEnumerable<string>|EmbeddingGenerationOptions|CancellationToken",
            "EmbeddingClient.GenerateEmbeddingsAsync|BinaryContent|RequestOptions",
            "EmbeddingClient.GenerateEmbeddingsAsync|IEnumerable<ReadOnlyMemory<int>>|EmbeddingGenerationOptions|CancellationToken",
            "EmbeddingClient.GenerateEmbeddingsAsync|IEnumerable<string>|EmbeddingGenerationOptions|CancellationToken",
            "FileUploadPurpose.Equals|FileUploadPurpose",
            "FileUploadPurpose.Equals|object",
            "FileUploadPurpose.GetHashCode",
            "FileUploadPurpose.operator !=|FileUploadPurpose|FileUploadPurpose",
            "FileUploadPurpose.operator ==|FileUploadPurpose|FileUploadPurpose",
            "FileUploadPurpose.operator implicit FileUploadPurpose|string",
            "FileUploadPurpose.ToString",
            "GeneratedImageFormat.Equals|GeneratedImageFormat",
            "GeneratedImageFormat.Equals|object",
            "GeneratedImageFormat.GetHashCode",
            "GeneratedImageFormat.operator !=|GeneratedImageFormat|GeneratedImageFormat",
            "GeneratedImageFormat.operator ==|GeneratedImageFormat|GeneratedImageFormat",
            "GeneratedImageFormat.operator implicit GeneratedImageFormat|string",
            "GeneratedImageFormat.ToString",
            "GeneratedImageQuality.Equals|GeneratedImageQuality",
            "GeneratedImageQuality.Equals|object",
            "GeneratedImageQuality.GetHashCode",
            "GeneratedImageQuality.operator !=|GeneratedImageQuality|GeneratedImageQuality",
            "GeneratedImageQuality.operator ==|GeneratedImageQuality|GeneratedImageQuality",
            "GeneratedImageQuality.operator implicit GeneratedImageQuality|string",
            "GeneratedImageQuality.ToString",
            "GeneratedImageSize.Equals|GeneratedImageSize",
            "GeneratedImageSize.Equals|object",
            "GeneratedImageSize.GetHashCode",
            "GeneratedImageSize.operator !=|GeneratedImageSize|GeneratedImageSize",
            "GeneratedImageSize.operator ==|GeneratedImageSize|GeneratedImageSize",
            "GeneratedImageSize.ToString",
            "GeneratedImageStyle.Equals|GeneratedImageStyle",
            "GeneratedImageStyle.Equals|object",
            "GeneratedImageStyle.GetHashCode",
            "GeneratedImageStyle.operator !=|GeneratedImageStyle|GeneratedImageStyle",
            "GeneratedImageStyle.operator ==|GeneratedImageStyle|GeneratedImageStyle",
            "GeneratedImageStyle.operator implicit GeneratedImageStyle|string",
            "GeneratedImageStyle.ToString",
            "GeneratedSpeechFormat.Equals|GeneratedSpeechFormat",
            "GeneratedSpeechFormat.Equals|object",
            "GeneratedSpeechFormat.GetHashCode",
            "GeneratedSpeechFormat.operator !=|GeneratedSpeechFormat|GeneratedSpeechFormat",
            "GeneratedSpeechFormat.operator ==|GeneratedSpeechFormat|GeneratedSpeechFormat",
            "GeneratedSpeechFormat.operator implicit GeneratedSpeechFormat|string",
            "GeneratedSpeechFormat.ToString",
            "GeneratedSpeechVoice.Equals|GeneratedSpeechVoice",
            "GeneratedSpeechVoice.Equals|object",
            "GeneratedSpeechVoice.GetHashCode",
            "GeneratedSpeechVoice.operator !=|GeneratedSpeechVoice|GeneratedSpeechVoice",
            "GeneratedSpeechVoice.operator ==|GeneratedSpeechVoice|GeneratedSpeechVoice",
            "GeneratedSpeechVoice.operator implicit GeneratedSpeechVoice|string",
            "GeneratedSpeechVoice.ToString",
            "ImageClient.GenerateImage|string|ImageGenerationOptions|CancellationToken",
            "ImageClient.GenerateImageAsync|string|ImageGenerationOptions|CancellationToken",
            "ImageClient.GenerateImageEdit|Stream|string|string|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEdit|Stream|string|string|Stream|string|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEdit|string|string|ImageEditOptions",
            "ImageClient.GenerateImageEdit|string|string|string|ImageEditOptions",
            "ImageClient.GenerateImageEditAsync|Stream|string|string|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEditAsync|Stream|string|string|Stream|string|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEditAsync|string|string|ImageEditOptions",
            "ImageClient.GenerateImageEditAsync|string|string|string|ImageEditOptions",
            "ImageClient.GenerateImageEdits|BinaryContent|string|RequestOptions",
            "ImageClient.GenerateImageEdits|Stream|string|string|int|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEdits|Stream|string|string|Stream|string|int|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEdits|string|string|int|ImageEditOptions",
            "ImageClient.GenerateImageEdits|string|string|string|int|ImageEditOptions",
            "ImageClient.GenerateImageEditsAsync|BinaryContent|string|RequestOptions",
            "ImageClient.GenerateImageEditsAsync|Stream|string|string|int|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEditsAsync|Stream|string|string|Stream|string|int|ImageEditOptions|CancellationToken",
            "ImageClient.GenerateImageEditsAsync|string|string|int|ImageEditOptions",
            "ImageClient.GenerateImageEditsAsync|string|string|string|int|ImageEditOptions",
            "ImageClient.GenerateImages|BinaryContent|RequestOptions",
            "ImageClient.GenerateImages|string|int|ImageGenerationOptions|CancellationToken",
            "ImageClient.GenerateImagesAsync|BinaryContent|RequestOptions",
            "ImageClient.GenerateImagesAsync|string|int|ImageGenerationOptions|CancellationToken",
            "ImageClient.GenerateImageVariation|Stream|string|ImageVariationOptions|CancellationToken",
            "ImageClient.GenerateImageVariation|string|ImageVariationOptions",
            "ImageClient.GenerateImageVariationAsync|Stream|string|ImageVariationOptions|CancellationToken",
            "ImageClient.GenerateImageVariationAsync|string|ImageVariationOptions",
            "ImageClient.GenerateImageVariations|BinaryContent|string|RequestOptions",
            "ImageClient.GenerateImageVariations|Stream|string|int|ImageVariationOptions|CancellationToken",
            "ImageClient.GenerateImageVariations|string|int|ImageVariationOptions",
            "ImageClient.GenerateImageVariationsAsync|BinaryContent|string|RequestOptions",
            "ImageClient.GenerateImageVariationsAsync|Stream|string|int|ImageVariationOptions|CancellationToken",
            "ImageClient.GenerateImageVariationsAsync|string|int|ImageVariationOptions",
            "ModerationClient.ClassifyText|BinaryContent|RequestOptions",
            "ModerationClient.ClassifyText|IEnumerable<string>|CancellationToken",
            "ModerationClient.ClassifyText|string|CancellationToken",
            "ModerationClient.ClassifyTextAsync|BinaryContent|RequestOptions",
            "ModerationClient.ClassifyTextAsync|IEnumerable<string>|CancellationToken",
            "ModerationClient.ClassifyTextAsync|string|CancellationToken",
            "OpenAIAudioModelFactory.AudioTranscription|string|TimeSpan?|string|IEnumerable<TranscribedWord>|IEnumerable<TranscribedSegment>",
            "OpenAIAudioModelFactory.AudioTranslation|string|TimeSpan?|string|IEnumerable<TranscribedSegment>",
            "OpenAIAudioModelFactory.TranscribedSegment|int|int|TimeSpan|TimeSpan|string|ReadOnlyMemory<int>|float|float|float|float",
            "OpenAIAudioModelFactory.TranscribedWord|string|TimeSpan|TimeSpan",
            "OpenAIChatModelFactory.ChatCompletion|string|ChatFinishReason|ChatMessageContent|string|IEnumerable<ChatToolCall>|ChatMessageRole|ChatFunctionCall|IEnumerable<ChatTokenLogProbabilityDetails>|IEnumerable<ChatTokenLogProbabilityDetails>|DateTimeOffset|string|string|ChatTokenUsage",
            "OpenAIChatModelFactory.ChatInputTokenUsageDetails|int|int",
            "OpenAIChatModelFactory.ChatOutputTokenUsageDetails|int",
            "OpenAIChatModelFactory.ChatOutputTokenUsageDetails|int|int",
            "OpenAIChatModelFactory.ChatTokenLogProbabilityDetails|string|float|ReadOnlyMemory<byte>?|IEnumerable<ChatTokenTopLogProbabilityDetails>",
            "OpenAIChatModelFactory.ChatTokenTopLogProbabilityDetails|string|float|ReadOnlyMemory<byte>?",
            "OpenAIChatModelFactory.ChatTokenUsage|int|int|int|ChatOutputTokenUsageDetails",
            "OpenAIChatModelFactory.ChatTokenUsage|int|int|int|ChatOutputTokenUsageDetails|ChatInputTokenUsageDetails",
            "OpenAIChatModelFactory.StreamingChatCompletionUpdate|string|ChatMessageContent|StreamingChatFunctionCallUpdate|IEnumerable<StreamingChatToolCallUpdate>|ChatMessageRole?|string|IEnumerable<ChatTokenLogProbabilityDetails>|IEnumerable<ChatTokenLogProbabilityDetails>|ChatFinishReason?|DateTimeOffset|string|string|ChatTokenUsage",
            "OpenAIChatModelFactory.StreamingChatFunctionCallUpdate|string|BinaryData",
            "OpenAIChatModelFactory.StreamingChatToolCallUpdate|int|string|ChatToolCallKind|string|BinaryData",
            "OpenAIClient.GetAudioClient|string",
            "OpenAIClient.GetChatClient|string",
            "OpenAIClient.GetEmbeddingClient|string",
            "OpenAIClient.GetImageClient|string",
            "OpenAIClient.GetModerationClient|string",
            "OpenAIClient.GetOpenAIFileClient",
            "OpenAIClient.GetOpenAIModelClient",
            "OpenAIEmbedding.ToFloats",
            "OpenAIEmbeddingsModelFactory.EmbeddingTokenUsage|int|int",
            "OpenAIEmbeddingsModelFactory.OpenAIEmbedding|int|IEnumerable<float>",
            "OpenAIEmbeddingsModelFactory.OpenAIEmbeddingCollection|IEnumerable<OpenAIEmbedding>|string|EmbeddingTokenUsage",
            "OpenAIFileClient.DeleteFile|string|CancellationToken",
            "OpenAIFileClient.DeleteFile|string|RequestOptions",
            "OpenAIFileClient.DeleteFileAsync|string|CancellationToken",
            "OpenAIFileClient.DeleteFileAsync|string|RequestOptions",
            "OpenAIFileClient.DownloadFile|string|CancellationToken",
            "OpenAIFileClient.DownloadFile|string|RequestOptions",
            "OpenAIFileClient.DownloadFileAsync|string|CancellationToken",
            "OpenAIFileClient.DownloadFileAsync|string|RequestOptions",
            "OpenAIFileClient.GetFile|string|CancellationToken",
            "OpenAIFileClient.GetFile|string|RequestOptions",
            "OpenAIFileClient.GetFileAsync|string|CancellationToken",
            "OpenAIFileClient.GetFileAsync|string|RequestOptions",
            "OpenAIFileClient.GetFiles|CancellationToken",
            "OpenAIFileClient.GetFiles|FilePurpose|CancellationToken",
            "OpenAIFileClient.GetFiles|string|RequestOptions",
            "OpenAIFileClient.GetFilesAsync|CancellationToken",
            "OpenAIFileClient.GetFilesAsync|FilePurpose|CancellationToken",
            "OpenAIFileClient.GetFilesAsync|string|RequestOptions",
            "OpenAIFileClient.UploadFile|BinaryContent|string|RequestOptions",
            "OpenAIFileClient.UploadFile|BinaryData|string|FileUploadPurpose",
            "OpenAIFileClient.UploadFile|Stream|string|FileUploadPurpose|CancellationToken",
            "OpenAIFileClient.UploadFile|string|FileUploadPurpose",
            "OpenAIFileClient.UploadFileAsync|BinaryContent|string|RequestOptions",
            "OpenAIFileClient.UploadFileAsync|BinaryData|string|FileUploadPurpose",
            "OpenAIFileClient.UploadFileAsync|Stream|string|FileUploadPurpose|CancellationToken",
            "OpenAIFileClient.UploadFileAsync|string|FileUploadPurpose",
            "OpenAIFilesModelFactory.FileDeletionResult|string|bool",
            "OpenAIFilesModelFactory.OpenAIFileCollection|IEnumerable<OpenAIFile>",
            "OpenAIFilesModelFactory.OpenAIFileInfo|string|int?|DateTimeOffset|string|FilePurpose|FileStatus|string",
            "OpenAIImagesModelFactory.GeneratedImage|BinaryData|Uri|string",
            "OpenAIImagesModelFactory.GeneratedImageCollection|DateTimeOffset|IEnumerable<GeneratedImage>",
            "OpenAIModelClient.DeleteModel|string|CancellationToken",
            "OpenAIModelClient.DeleteModel|string|RequestOptions",
            "OpenAIModelClient.DeleteModelAsync|string|CancellationToken",
            "OpenAIModelClient.DeleteModelAsync|string|RequestOptions",
            "OpenAIModelClient.GetModel|string|CancellationToken",
            "OpenAIModelClient.GetModel|string|RequestOptions",
            "OpenAIModelClient.GetModelAsync|string|CancellationToken",
            "OpenAIModelClient.GetModelAsync|string|RequestOptions",
            "OpenAIModelClient.GetModels|CancellationToken",
            "OpenAIModelClient.GetModels|RequestOptions",
            "OpenAIModelClient.GetModelsAsync|CancellationToken",
            "OpenAIModelClient.GetModelsAsync|RequestOptions",
            "OpenAIModelsModelFactory.ModelDeletionResult|string|bool",
            "OpenAIModelsModelFactory.OpenAIModel|string|DateTimeOffset|string",
            "OpenAIModelsModelFactory.OpenAIModelCollection|IEnumerable<OpenAIModel>",
            "OpenAIModerationsModelFactory.ModerationCategory|bool|float",
            "OpenAIModerationsModelFactory.ModerationResult|bool|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory|ModerationCategory",
            "OpenAIModerationsModelFactory.ModerationResultCollection|string|string|IEnumerable<ModerationResult>",
        };

        private static readonly HashSet<string> _OPENAICUA001AttributeTypes = new(StringComparer.OrdinalIgnoreCase)
        {
            "ComputerCallAction",
            "ComputerCallActionKind",
            "ComputerCallActionMouseButton",
            "ComputerCallOutputResponseItem",
            "ComputerCallOutputStatus",
            "ComputerCallResponseItem",
            "ComputerCallSafetyCheck",
            "ComputerCallStatus",
            "ComputerCallOutput",
            "ComputerToolEnvironment",
        };

        protected override PropertyProvider? VisitProperty(PropertyProvider property)
        {
            // Skip properties that are already marked as experimental
            if (property.Attributes.Any(attr => attr.Type.Equals(typeof(ExperimentalAttribute))))
            {
                return base.VisitProperty(property);
            }

            // Skip properties that are not public or are in non-stable classes
            if ((!property.Modifiers.HasFlag(MethodSignatureModifiers.Public) &&
                    !property.Modifiers.HasFlag(MethodSignatureModifiers.Protected)) ||
                !_stableClasses.Contains($"{property.EnclosingType.Type.Namespace}.{property.EnclosingType.Name}"))
            {
                return base.VisitProperty(property);
            }

            if (!_stableProperties.Contains($"{property.EnclosingType.Name}.{property.Name}"))
            {
                property.Update(
                    attributes: [.. property.Attributes,
                        property.EnclosingType.Type.Namespace.StartsWith(_realtimeNamespace) ? _experimental002Attribute : _experimental001Attribute]);

                return property;
            }

            return base.VisitProperty(property);
        }

        protected override MethodProvider? VisitMethod(MethodProvider methodProvider)
        {
            // Skip methods that are not public or are in non-stable classes
            if ((!methodProvider.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public) &&
                    !methodProvider.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Protected)) ||
                !_stableClasses.Contains($"{methodProvider.EnclosingType.Type.Namespace}.{methodProvider.EnclosingType.Name}"))
            {
                return base.VisitMethod(methodProvider);
            }

            string lookupName = methodProvider.Signature.Parameters.Count switch
            {
                0 => $"{methodProvider.Signature.Name}",
                1 => $"{methodProvider.Signature.Name}|{methodProvider.Signature.Parameters[0].Type.Name}",
                _ => $"{methodProvider.Signature.Name}|{string.Join("|", methodProvider.Signature.Parameters.Select(p => p.Type.Name))}"
            };

            // Generate a lookup name based on method signature
            string operatorPrefix = "operator ";
            bool isOperator = methodProvider.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Operator);
            bool isImplicit = methodProvider.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Implicit);
            bool isExplicit = methodProvider.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Explicit);
            lookupName = $"{methodProvider.EnclosingType.Name}.{(isOperator ? operatorPrefix : "")}{(isImplicit ? $"implicit {methodProvider.EnclosingType.Name}" : "")}{lookupName}";

            if (!_stableMethods.Contains(lookupName))
            {
                methodProvider.Signature.Update(
                    attributes: [.. methodProvider.Signature.Attributes,
                        methodProvider.EnclosingType.Type.Namespace.StartsWith(_realtimeNamespace) || (methodProvider.Signature.ReturnType?.Namespace.StartsWith(_realtimeNamespace) ?? false) ?
                            _experimental002Attribute :
                            _experimental001Attribute]);

                return methodProvider;
            }

            return base.VisitMethod(methodProvider);
        }

        protected override TypeProvider? VisitType(TypeProvider type)
        {
            if ((type.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public) ||
                    type.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Protected)) &&
                !_stableClasses.Contains($"{type.Type.Namespace}.{type.Name}") &&
                (type is ClientProvider or ModelProvider or ClientOptionsProvider or EnumProvider))
            {
                AttributeStatement experimentalAttribute = type.Type.Namespace switch
                {
                    _ when type.Type.Namespace.StartsWith(_realtimeNamespace) => _experimental002Attribute,
                    _ when _OPENAICUA001AttributeTypes.Contains(type.Name) => _experimentalCUA001Attribute,
                    _ => _experimental001Attribute
                };
                type.Update(
                    attributes: [.. type.Attributes,
                        experimentalAttribute]);

                return type;
            }

            return base.VisitType(type);
        }
    }
}
